#include "channel_manager.h"
#include "log.h"
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <asio.hpp>

void ChannelManager::StartListen(int32_t port, CTcpSpi* pSvrSpi)
{
	m_working = true;
	acceptor_ = new asio::ip::tcp::acceptor(m_ioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
	if (acceptor_)
	{
		LOGI("Listen on port:%d",port);
	}
	m_pSvrSpi = pSvrSpi;
	DoAccept();
	
	if (!m_io_thread.native_handle())
	{
		StartIoThread();
	}
}

void ChannelManager::DoAccept()
{
	acceptor_->async_accept([this](asio::error_code ec, asio::ip::tcp::socket socket)
	{
		if (!ec)
		{
			std::string ip = socket.remote_endpoint().address().to_string();
			int32_t port = socket.remote_endpoint().port();
			LOGD("connected from [%s:%d]",ip.c_str(),port);
			auto channel = std::make_shared<Channel>(this, std::move(socket), &m_ioContext, ChannalType::TCPSERVER);
			channel->StartRead();//连接已建立，等待客户端发起握手消息
		}
		DoAccept();
	});
}

void ChannelManager::StartIoThread()
{
	m_io_thread = std::thread([this]()
	{
		while (m_working)
		{
			m_ioContext.run();
		}
	});
}

void ChannelManager::Stop()
{
	if(m_working)
	{
		m_working = false;
		m_ioContext.stop();
		m_io_thread.join();
	}
}

bool ChannelManager::SendToAllService(const char *pData, int nDataLen)
{
	{
		std::lock_guard<std::mutex> lk(m_ChannelsLock);
		for (auto& s : m_Channels)
		{
			s.second->SendMsg(pData, nDataLen);
		}
	}

	return true;
}

bool ChannelManager::SendByChannelID(int nChannelID, const char *pData, int64_t nDataLen)
{
	std::shared_ptr<Channel> channel;
	{
		std::lock_guard<std::mutex> lk(m_ChannelsLock);
		auto it = m_Channels.find(nChannelID);
		if (it == m_Channels.end())
		{
			LOGE("cannot find channel by nChannelID");
			return false;
		}
		channel = it->second;
	}
	channel->SendMsg(pData, nDataLen);
	return true;
}



bool ChannelManager::Connect(const std::string& strHost, int32_t iPort, CTcpSpi* pCliSpi, bool bAutoReLink)
{
	m_pCliSpi = pCliSpi;
	m_bAutoReLink = bAutoReLink;
	return InternalConnect(strHost, iPort);
}

bool ChannelManager::InternalConnect(const std::string & strHost, int32_t iPort)
{
	asio::ip::tcp::resolver resolver(m_ioContext);
	auto endpoints = resolver.resolve(strHost, std::to_string(iPort));
	auto pConnectSocket = new asio::ip::tcp::socket(m_ioContext);
	auto pEndPoint = new asio::ip::tcp::resolver::results_type();
	*pEndPoint = endpoints;
	std::shared_ptr<asio::steady_timer> pTimer = std::make_shared<asio::steady_timer>(m_ioContext);
	DoConnect(pConnectSocket, pEndPoint, pTimer);
	m_working = true;
	if (!m_io_thread.native_handle())
	{
		StartIoThread();
	}

	return true;
}

void ChannelManager::DoConnect(asio::ip::tcp::socket *pConnectSocket,
							asio::ip::tcp::resolver::results_type *pEndPoint, 
								std::shared_ptr<asio::steady_timer> pTimer)
{
	asio::async_connect(*pConnectSocket, *pEndPoint,[this, pConnectSocket, pEndPoint, pTimer]
	(asio::error_code ec, asio::ip::tcp::endpoint)
	{
		if (ec) 
		{
			LOGI("connect failed,error code[%d:%s]",ec.value(),ec.message().c_str());
			// 再次重连,当然这里可以对错误进行判断，是可以重连再重连
			if (m_bAutoReLink)
			{
				pTimer->expires_from_now(std::chrono::seconds(2));
				pTimer->async_wait([this, pConnectSocket, pEndPoint, pTimer](const asio::error_code& ec)
				{
					DoConnect(pConnectSocket, pEndPoint, pTimer);
				});
			}
			else
			{
				LOGD("auto relink is %s",m_bAutoReLink?"true":"false");
			}
		}
		else 
		{
			//连接已建立
			auto channel = std::make_shared<Channel>(this, std::move(*pConnectSocket), &m_ioContext, ChannalType::TCPCLIENT);
			channel->DoReqShakeHand();
			if (pConnectSocket)
			{
				delete pConnectSocket;
			}
			if (pEndPoint)
			{
				delete pEndPoint;
			}
// 			if (pTimer)
// 			{
// 				delete pTimer;
// 			}
		}
	});
}

int ChannelManager::AddService(std::shared_ptr<Channel> s)
{
	std::lock_guard<std::mutex> lk(m_ChannelsLock);
	for (int i = 0; ; i++)
	{
		auto it = m_Channels.find(i);
		if (it == m_Channels.end())
		{
			s->SetChannelID(i);
			m_Channels.emplace(i,s);
			return i;
		}
	}
	//return -1;
}

// 移除会话
void ChannelManager::DeleteService(std::shared_ptr<Channel> s)
{
	{
		std::lock_guard<std::mutex> lk(m_ChannelsLock);
		if (m_Channels.find(s->GetChannelID()) != m_Channels.end())
		{
			m_Channels.erase(s->GetChannelID());
		}
	}
}

int32_t ChannelManager::AddTimer(int id, int ms, mfvoid cb)
{
	auto pTimer = std::make_shared<asio::steady_timer>(m_ioContext);
	pTimer->expires_from_now(std::chrono::milliseconds(ms));
	pTimer->async_wait([this, pTimer,cb](const asio::error_code& ec)
	{
		if (cb)
		{
			cb();
		}
	});
	m_TimerMap.emplace(id, pTimer);
	Timer(id, ms, cb);
	return 0;
}

void ChannelManager::Timer(int id, int ms, mfvoid cb)
{
	auto it = m_TimerMap.find(id);
	if (it != m_TimerMap.end())
	{
		it->second->expires_from_now(std::chrono::milliseconds(ms));
		it->second->async_wait([this, id, ms, cb](const asio::error_code& ec)
		{
			if (cb)
			{
				cb();
			}
			Timer(id, ms, cb);
		});
	}
	
}

int32_t ChannelManager::RemoveTimer(int id)
{
	m_TimerMap.erase(id);
	return 0;
}